package org.cncy;

import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import javax.net.ssl.*;
import java.io.IOException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


@Slf4j
public class OkhttpUtil {
    private static volatile OkHttpClient okHttpClient = null;
    private String url;
    private Request.Builder request = null;
    private Map<String, String> parameters;
    private Map<String, String> header;
    private static Method method;
    private byte[] bytes;
    private Map<String, String> form;
    private FormBody formBody;
    private String jsonBody;
    private RequestBody requestBody;

    enum Method {
        get, post, put
    }

    private OkhttpUtil() {
        if (okHttpClient == null) {
            synchronized (OkhttpUtil.class) {
                okHttpClient = getOkHttpClient();
            }
        }
        if (null == request) {
            synchronized (OkhttpUtil.class) {
                request = new Request.Builder();
            }
        }
    }

    public static OkhttpUtil builder() {
        return new OkhttpUtil();
    }


    public OkhttpUtil url(String url) {
        this.url = url;
        return this;
    }

    public OkhttpUtil addHeader(String k, String v) {
        if (header == null) {
            header = new HashMap<>();
        }
        header.put(k, v);
        return this;
    }

    public OkhttpUtil addParamter(String k, String v) {
        if (parameters == null) {
            parameters = new HashMap<>();
        }
        parameters.put(k, v);
        return this;
    }

    public OkhttpUtil addForm(String k, String v) {
        if (form == null) {
            form = new HashMap<>();
        }
        form.put(k, v);
        return this;
    }

    public OkhttpUtil body(String body) {
        this.jsonBody = body;
        return this;
    }

    public OkhttpUtil body(byte[] bytes) {
        this.bytes = bytes;
        return this;
    }


    public OkhttpUtil get() {
        method = Method.get;
        return this;
    }

    public OkhttpUtil post() {
        method = Method.post;
        return this;
    }

    public OkhttpUtil put() {
        method = Method.put;
        return this;
    }

    /**
     * 组装 header
     */
    private void assembleHeader() {
        if (Objects.isNull(header)) return;
        header.forEach(request::addHeader);
    }

    /**
     * 组装requestParam
     */
    private void assembleParameters() {
        //todo 如果requestParam =null 直接返回否则就进行配置pathParam
        if (Objects.isNull(parameters)) return;

        HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
        parameters.forEach(urlBuilder::addQueryParameter);

        //todo param不为空的时候改变url 如果没有param 那么还用原来的url
        url = urlBuilder.build().toString();
    }

    /**
     * 组装formBody
     */
    private void assembleFormBody() {
        if (Objects.isNull(form)) return;

        FormBody.Builder builder = new FormBody.Builder();
        form.forEach(builder::add);
        formBody = builder.build();
    }

    /**
     * * 根据不通的请求方法进行不通的数据组装jsonBody
     */
    public void assembleRequestBody() {
        //todo 如果两个都为空直接返回 ， 如果jsonBody不为空就已jsonBody创建请求体否则就创建bytes 请求体
        if (null == jsonBody && null == bytes) return;
        requestBody = jsonBody != null ? RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonBody) : MultipartBody.create(null, bytes);
    }

    /**
     * 组装reqeust
     */
    public void assembleRequest() {

        request.url(url);
        if (null == requestBody && null == formBody) {
            //todo 这里只配置post,put等请求方法，如果请求体为空会报错
            switch (method) {
                case post:
                    request.post(new FormBody.Builder().build());
                    break;
                case put:
                    request.put(new FormBody.Builder().build());
                    break;
            }
        } else {
            //todo 这里添加请求体
            if (null != requestBody) {
                switch (method) {
                    case post:
                        request.post(requestBody);
                        break;
                    case put:
                        request.put(requestBody);
                        break;
                }
            }


            if (null != formBody) {
                switch (method) {
                    case post:
                        request.post(formBody);
                        break;
                    case put:
                        request.put(formBody);
                        break;
                }
            }
        }
    }

    /**
     * 同步调用
     * @return
     */
    public String execute() {
        assembleHeader(); //TODO 2024/11/1:组装请求头
        assembleParameters();//TODO 2024/11/1:组装parameters
        assembleFormBody();//TODO 2024/11/1:组装form 表单
        assembleRequestBody();//TODO 2024/11/1:组装请求体
        assembleRequest();//TODO 2024/11/1:发送请求

        try {
            Response response = okHttpClient.newCall(request.build()).execute();
            assert response.isSuccessful();
            return response.body().string();
        } catch (IOException e) {
            e.printStackTrace();
            return "请求失败:" + e.getMessage();
        }
    }

    /**
     * 异步调用
     * @param callback
     */
    public void enqueue(Callback callback) {
        assembleHeader(); //组装请求头
        assembleParameters(); //组装requestParam
        assembleFormBody(); //组装formBody
        assembleRequestBody(); //组装jsonBody
        assembleRequest();  //组装请求
        okHttpClient.newCall(request.build()).enqueue(callback);
    }

    public OkHttpClient getOkHttpClient() {

        Interceptor interceptor = (chain) -> {
            Request request = chain.request();
            log.debug("request = {}", request);

            log.debug("request headers = {}", request.headers());

            String url = request.url().toString();
            log.debug("intercept: proceed start: url = " + url + ", at " + DateFormateUtil.getyMdHms(System.currentTimeMillis()));
            Response response = chain.proceed(request);
            log.debug("response = {}", response);

            log.debug("response headers = {}", response.headers());
            log.debug("intercept: proceed end: url = " + url + ", at " + DateFormateUtil.getyMdHms(System.currentTimeMillis()));
            return response;
        };


        return new OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS)
                //.writeTimeout(1, TimeUnit.SECONDS)
                // TODO: 2024/3/19  必须要这么写
                .sslSocketFactory(getSSLSocketFactory(), getX509TrustManager())
                //必须要这一步，不可忽略
                .hostnameVerifier(getHostnameVerifier())
                // 默认开
                // .followRedirects(true)
                .retryOnConnectionFailure(true)
                //.followSslRedirects(true) 默认开
                .addInterceptor(interceptor).build();
    }

    //获取这个SSLSocketFactory
    private SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //获取TrustManager
    private TrustManager[] getTrustManager() {
        return new X509TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }
        }};
    }

    //获取HostnameVerifier
    private HostnameVerifier getHostnameVerifier() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };
    }

    private X509TrustManager getX509TrustManager() {
        X509TrustManager trustManager = null;
        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
            }
            trustManager = (X509TrustManager) trustManagers[0];
        } catch (Exception e) {
            e.printStackTrace();
        }

        return trustManager;
    }


}
